Circuit explanation:
The power supply for the circuit is taken from the start battery, using a 78L05 voltage regulator.
The voltage measured is that from the comfortbattery or the combination comfort/start battery (bridge on). The
voltage is measured with the aid of a voltage divider constructed of R3/R4 to stay within a 5V limit. The maximum
input voltage is 15V (enough for the situation where the battery is fully charged and the sun shines).
When the voltage drops below 12,4 volts, the brigde will be put off. When the voltage rises above 13,5 volts, the
bridge will be put on. Between these two values the bridge state is left as-is (which can be either on or off,
depending from where we are coming from).
The operation of the circuit depends on the use of 2 opamps which compare the voltage against a preset voltage.
One opamp is used for the "low" treshold, the other one is used for the "high" treshold. The result will always be
0 or 1 (below or above treshold) and the two outputs from the opamps are connected to the input of a microcontroller
which will handle the control of the bridge programmatically.
The two opamps can be found in IC LM324 which has four of them, so that should be enough.
The low treshold and the high treshold should be set at around 12,4 and 13,5 volts resp. Adjustment can occur via POT1
and POT2.
The bridge itself consists of a so-called bi-stable relay which has two coils (one for off and one for on). This relay
has a coil voltage of 12V and a switching current of 12A at 12V.
The switching contacts of the bridge are not shown on the schematic diagram, you just should use the relays make-contact
and connect one end of it with the start battery, the other end goes to the comfort battery.
A bi-stable relay does not use any current no matter in which position it is (bridge on or bridge off). Only during
changing states you need a current for a short while (about 0,2 seconds pulse).
Both coils of the bi-stable relay are powered using a BC337 transistor, which as capable of doing this job.
Notice that the coils are in the 12V circuit, not in the 5V circuit.
Relay 3 in the schematic diagram is the "off" coil, Relay 2 is the "on" coil.
Relay 1, which is not strictly necessary, is used to disable an external alarm system while switching, for a short
while. This is done only because I have an alarm system that is triggered by sudden voltage changes. You can do
without Relay 1 if you don't need such an interface with your alarm system.
You can also operate the bridge manually using pushbutton SW1. This works only when the voltage is in the floating window,
that is between 12,4 and 13,5 volts.
Like mentioned before, the relay is a bi-stable relay. You should use a heavy model, for instance Conrad ordering nbr.
504173 - 62.
This one has a switching capability of 16 amps. The software takes care of the pulse operation.
If you can find a bi-stable relay that has an even higher switching capability of say 30 amps (which I couldn't), you
might use the circuit for making the bridge while driving. In that case however, a software adjustment is necessary.
Two LEDs (with current limiting resistor) can come in handy when connected to both coils of the bi-stable relay (this
is not shown in the diagram). Then you can see when bridging/de-bridging occurs (LED whill shortly light).
;;
;;
;; Zonnebatterij automaat, a PIC 16F84 program to switch two lead-acid batteries in parallel
;; when there is enough sunlight.
;;
;; Copyright (C) 2000 Geert Van Espen (geert@vanespen.com)
;;
;;
LIST P=16F84
errorlevel 0,-305
processor 16f84
include "p16f84.inc"
__CONFIG _CP_OFF & _RC_OSC & _PWRTE_ON & _WDT_ON
;
;
;
;
; commando om dit project naar de pic te sturen:
; TOPIC -RWG SOLARSW.HEX
;
;
;
;
;
; current settings: low level = 12.29 V
; high level = 13.45 V
;
; general purpose register allocation
werk equ 0x0c ; werkregister
t1 equ 0x0d ; telwerk 1
t2 equ 0x0e ; telwerk 2
TIJDl equ 0x10 ; to keep track of time
TIJDh equ 0x11
ddd equ 0x13 ; debug only
KLAPPERTIJD0 equ 0x14 ; hysteresis 0
KLAPPERTIJD1 equ 0x15 ; hysteresis 1
KLAPPERTIJD2 equ 0x16 ; hysteresis 2
KLAPPERTIJD3 equ 0x17 ; hysteresis 3
RB0_STAND equ 0x18 ; 0=uit 1=aan
RB1_STAND equ 0x19 ; 0=uit 1=aan
RB2_STAND equ 0x1a ; 0=uit 1=aan
RB3_STAND equ 0x1b ; 0=uit 1=aan
tempvar equ 0x1c ;
Wl equ 0x20 ;
Wh equ 0x21 ;
BRUG equ 0x22 ; 0 = geen doorkoppeling batterijen
; 1 = wel doorkoppeling batterijen
CONTACT equ 0x23 ; 0 = contactsleutel uit
; 1 = contactsleutel aan
PrevRB0 equ 0x24 ; vorige RB0 situatie
PrevRB1 equ 0x25 ; vorige RB1 situatie
PrevRB2 equ 0x26 ; vorige contact situatie
PrevBtn equ 0x27 ; vorige pushbutton situatie
; tijdconstanten
;;;;;;;;;;;;;;;;
m_input_a equ b'00000000' ; input mask, which bits are input
m_input_b equ b'11111111' ;
org 0x00 ; reset vector
goto main
dt "2000-10-10 geert@vanespen.com Zonnebatterij automaat"
;***********************************************************************
;* *
;* initialise *
;* *
;***********************************************************************
initialise: ; set up ports
MOVLW 0 ;
TRIS 5 ;
MOVLW 0x0F ;
TRIS 6 ;
MOVLW 0x80 ;
OPTION ;
clrf TIJDl ; initialisaties
clrf TIJDh ;
clrf KLAPPERTIJD0 ;
clrf KLAPPERTIJD1 ;
clrf KLAPPERTIJD2 ;
clrf KLAPPERTIJD3 ;
clrf RB0_STAND ;
clrf RB1_STAND ;
clrf RB2_STAND ;
clrf RB3_STAND ;
clrf BRUG ;
clrf CONTACT ;
clrf PrevRB0 ;
clrf PrevRB1 ;
clrf PrevRB2 ;
clrf PrevBtn ;
clrf PORTA ; UIT laag
clrf PORTB
; uitgang even aan en uit laten knipperen om te laten zien dat het
; apparaat werkt
movlw 0xFE ; startwaarde
movwf PORTA ;
movlw 6 ; knipper 3 keer
movwf t1 ;
functiontest:
movlw 0x19 ; wacht halve seconde
call wachtlus ;
movlw 0xFF ;
xorwf PORTA, f ;
decfsz t1, f ;
goto functiontest ;
clrf PORTA ; UIT laag
retlw 0
;***********************************************************************
;* *
;* wachtlus *
;* *
;***********************************************************************
wachtlus:
; wacht x keer 18 milliseconden, met x in w
movwf werk ; zet w in werk
; debug only
; movlw 1
; movwf werk
slapertje:
sleep ;
decfsz werk, f ;
goto slapertje ;
retlw 0 ;
;***********************************************************************
;* *
;* test_portRB0 *
;* *
;***********************************************************************
; hysteresis voor poort RB0: als RB0 een tijdje 0 is wordt RB0_STAND
; ook 0; als RB0 1 is wordt RB0_STAND onmiddellijk 1
test_portRB0:
movf PORTB, w ; w = PORTB
andlw 1 ; we willen alleen RB0
movwf tempvar ;
movf tempvar, w ;
btfss tempvar, 0 ;
goto test0_low ;
movlw 1 ;
movwf RB0_STAND ;
clrf KLAPPERTIJD0 ; KLAPPERTIJD0 = 0
retlw 0 ;
test0_low:
subwf RB0_STAND, w ;
btfss STATUS, Z ; RB0 input = RB0_STAND?
goto test0_hyst1 ;
clrf KLAPPERTIJD0 ; ja: KLAPPERTIJD0 = 0
goto test0_hyst2 ;
test0_hyst1:
incf KLAPPERTIJD0, f ; neen: verhoog KLAPPERTIJD0
test0_hyst2:
btfss KLAPPERTIJD0, 2 ; KLAPPERTIJD0 hoog?
goto test0_hyst3 ; neen: gedaan
movf tempvar, w ;
movwf RB0_STAND ; ja: RB0_STAND = RB0 input
clrf KLAPPERTIJD0 ; KLAPPERTIJD0 = 0
test0_hyst3:
retlw 0 ;
;***********************************************************************
;* *
;* test_portRB1 *
;* *
;***********************************************************************
; hysteresis voor poort RB1: als RB1 een tijdje 0 is wordt RB1_STAND
; ook 0; als RB1 1 is wordt RB1_STAND onmiddellijk 1
test_portRB1:
movf PORTB, w ; w = PORTB
andlw 2 ; we willen alleen RB1
movwf tempvar ;
bcf STATUS, C ;
rrf tempvar, f ;
movf tempvar, w ;
btfss tempvar, 0 ;
goto test1_low ;
movlw 1 ;
movwf RB1_STAND ;
clrf KLAPPERTIJD1 ; KLAPPERTIJD1 = 0
retlw 0 ;
test1_low:
subwf RB1_STAND, w ;
btfss STATUS, Z ; RB1 input = RB1_STAND?
goto test1_hyst1 ;
clrf KLAPPERTIJD1 ; ja: KLAPPERTIJD1 = 0
goto test1_hyst2 ;
test1_hyst1:
incf KLAPPERTIJD1, f ; neen: verhoog KLAPPERTIJD1
test1_hyst2:
btfss KLAPPERTIJD1, 2 ; KLAPPERTIJD1 hoog?
goto test1_hyst3 ; neen: gedaan
movf tempvar, w ;
movwf RB1_STAND ; ja: RB1_STAND = RB1 input
clrf KLAPPERTIJD1 ; KLAPPERTIJD1 = 0
test1_hyst3:
retlw 0 ;
;***********************************************************************
;* *
;* test_portRB2 *
;* *
;***********************************************************************
; hysteresis voor poort RB2: als RB2 een tijdje 1 is wordt RB2_STAND
; ook 1; als RB2 een tijdje nul is wordt RB2_STAND ook nul
test_portRB2:
movf PORTB, w ; w = PORTB
andlw 0x04 ; we willen alleen RB2
movwf tempvar ;
bcf STATUS, C ;
rrf tempvar, f ;
rrf tempvar, f ;
movf tempvar, w ;
subwf RB2_STAND, w ;
btfss STATUS, Z ; RB2 input = RB2_STAND?
goto test2_hyst1 ;
clrf KLAPPERTIJD2 ; ja: KLAPPERTIJD2 = 0
goto test2_hyst2 ;
test2_hyst1:
incf KLAPPERTIJD2, f ; neen: verhoog KLAPPERTIJD2
test2_hyst2:
btfss KLAPPERTIJD2, 1 ; KLAPPERTIJD2 hoog?
goto test2_hyst3 ; neen: gedaan
movf tempvar, w ;
movwf RB2_STAND ; ja: RB2_STAND = RB2 input
clrf KLAPPERTIJD2 ; KLAPPERTIJD2 = 0
test2_hyst3:
retlw 0 ;
;***********************************************************************
;* *
;* test_portRB3 *
;* *
;***********************************************************************
; hysteresis voor poort RB3: als RB3 een tijdje 1 is wordt RB3_STAND
; ook 1; als RB3 een tijdje nul is wordt RB3_STAND ook nul
test_portRB3:
movf PORTB, w ; w = PORTB
andlw 0x08 ; we willen alleen RB3
movwf tempvar ;
bcf STATUS, C ;
rrf tempvar, f ;
rrf tempvar, f ;
rrf tempvar, f ;
movf tempvar, w ;
subwf RB3_STAND, w ;
btfss STATUS, Z ; RB3 input = RB3_STAND?
goto test3_hyst1 ;
clrf KLAPPERTIJD3 ; ja: KLAPPERTIJD3 = 0
goto test3_hyst2 ;
test3_hyst1:
incf KLAPPERTIJD3, f ; neen: verhoog KLAPPERTIJD3
test3_hyst2:
btfss KLAPPERTIJD3, 2 ; KLAPPERTIJD3 hoog?
goto test3_hyst3 ; neen: gedaan
movf tempvar, w ;
movwf RB3_STAND ; ja: RB3_STAND = RB3 input
clrf KLAPPERTIJD3 ; KLAPPERTIJD3 = 0
test3_hyst3:
retlw 0 ;
;***********************************************************************
;***********************************************************************
;* *
;* programma *
;* *
;***********************************************************************
main:
btfsc STATUS, NOT_TO ; if this is a reset or power-up ...
call initialise ; set port drivers and WDT prescaler
movlw 1 ; veronderstel contact aan
movwf CONTACT ;
movlw 3 ;
movwf PrevRB0 ;
movwf PrevRB1 ;
movwf PrevRB2 ;
movwf PrevBtn ;
start:
main_loop:
;
; LAGE DREMPELSPANNING (poort RB0)
;
call test_portRB0 ; test spanning op poort RB0
movf PrevRB0, w ; test of RB0_STAND veranderd is
subwf RB0_STAND, w ;
;;;;; btfsc STATUS, Z ; is de stand veranderd?
;;;;; goto test0_af ;
; veranderd
movf RB0_STAND, w ; ja, de stand is veranderd
movwf PrevRB0 ;
btfsc RB0_STAND, 0 ; is spanning laag?
goto test0_af ; neen, gedaan
btfss BRUG, 0 ; ja: test of brug al af stond
goto test0_af ; brug af: gedaan
bsf PORTA, 2 ; spanning laag: alarm-disable relais
bsf PORTA, 0 ; en RA0 een tijdje aanzetten
movlw 0xc ; wacht fractie seconde
call wachtlus ;
clrf BRUG ; bewaar huidige toestand
bcf PORTA, 0 ; poorten terug uitzetten
bcf PORTA, 2 ;
movlw 0x6f ; wacht 2 seconden
call wachtlus ;
test0_af:
;
; HOGE DREMPELSPANNING (poort RB1)
;
btfsc CONTACT, 0 ; als contact aan,
goto test1_af ; dan nooit brug maken!
call test_portRB1 ; test spanning op poort RB1
movf PrevRB1, w ;
subwf RB1_STAND, w ;
;;;;; btfsc STATUS, Z ; is de stand veranderd?
;;;;; goto test1_af ;
; veranderd
movf RB1_STAND, w ; ja, de stand is veranderd
movwf PrevRB1 ;
btfss RB1_STAND, 0 ; is spanning hoog?
goto test1_af ; neen, gedaan
btfsc BRUG, 0 ; ja: test of brug al aan stond
goto test1_af ; brug aan: gedaan
bsf PORTA, 2 ; spanning hoog: alarm-disable relais
bsf PORTA, 1 ; en RA1 een tijdje aanzetten
movlw 0xc ; wacht fractie seconde
call wachtlus ;
bsf BRUG, 0 ; bewaar huidige toestand
bcf PORTA, 1 ; poorten terug uitzetten
bcf PORTA, 2 ;
movlw 0x6f ; wacht 2 seconden
call wachtlus ;
test1_af:
;
; CONTACTSLEUTEL (poort RB2)
;
ContactSleutel:
call test_portRB2 ; test spanning op poort RB2
movf PrevRB2, w ;
subwf RB2_STAND, w ;
btfsc STATUS, Z ; is de stand veranderd?
goto test2_af ;
; veranderd
movf RB2_STAND, w ; ja, de stand is veranderd
movwf PrevRB2 ;
btfsc RB2_STAND, 0 ; is contact aan?
goto test2_aan ;
clrf CONTACT ; neen: bewaar huidige toestand
bsf PrevRB0, 3 ; herinitialiseer spanningstesten
bsf PrevRB1, 3 ;
goto test2_af ;
test2_aan:
bsf CONTACT, 0 ; bewaar huidige toestand
bsf PORTA, 2 ; alarm-disable relais en
bsf PORTA, 0 ; RA0 een tijdje aanzetten
movlw 0xc ; wacht fractie seconde
call wachtlus ;
clrf BRUG ; bewaar huidige toestand
bcf PORTA, 0 ; poorten terug uitzetten
bcf PORTA, 2 ;
movlw 0xfe ; wacht 5 seconden
call wachtlus ;
test2_af:
;
; PUSHBUTTON (poort RB3)
;
btfss CONTACT, 0 ; als contact aan,
goto PushButton ; dan nooit brug maken!
clrf PrevBtn ;
goto PushButton_samen;
PushButton:
call test_portRB3 ; als pushbutton gedrukt is,
movf PrevBtn, w ; dan toggelen
subwf RB3_STAND, w ;
btfsc STATUS, Z ; is de stand veranderd?
goto PushButton_gelijk;
PushButton_changed:
movf RB3_STAND, w ; ja: PrevBtn = RB3_STAND
movwf PrevBtn ;
btfss RB3_STAND, 0 ; is pushbutton gedrukt?
goto PushButton_af ;
movlw 1 ; ja: toggle BRUG
xorwf BRUG, f ;
; reflect BRUG value onto output ports
btfss BRUG, 0 ; BRUG waarde testen
goto PushButton_Reflect_uit;
PushButton_Reflect_aan:
bsf PORTA, 2 ; alarm-disable relais en
bsf PORTA, 1 ; RA1 een tijdje aanzetten
movlw 0xc ; wacht fractie seconde
call wachtlus ;
bcf PORTA, 1 ; poorten terug uitzetten
bcf PORTA, 2 ;
goto PushButton_samen;
PushButton_Reflect_uit:
bsf PORTA, 2 ; alarm-disable relais en
bsf PORTA, 0 ; RA0 een tijdje aanzetten
movlw 0xc ; wacht fractie seconde
call wachtlus ;
bcf PORTA, 0 ; poorten terug uitzetten
bcf PORTA, 2 ;
goto PushButton_samen;
PushButton_samen:
PushButton_gelijk:
goto PushButton_af ;
PushButton_af:
movlw 0x2 ; wacht fractie seconde
call wachtlus ;
goto start ;
;;
;;
end
Download source code (assembler) Download hex file (machine code)
